Skip to content

Replace Encodable implementations with Pydantic#584

Draft
eb8680 wants to merge 58 commits intomasterfrom
eb-pydantic-encodable
Draft

Replace Encodable implementations with Pydantic#584
eb8680 wants to merge 58 commits intomasterfrom
eb-pydantic-encodable

Conversation

@eb8680
Copy link
Contributor

@eb8680 eb8680 commented Feb 23, 2026

This PR is another work-in-progress attempt at systematically fixing the myriad issues with Encodable. It preserves the shell of the Encodable API but breaks up the concrete Encodable implementations into custom validation, serialization and schema generation overrides within Pydantic's existing APIs that are injected by rewriting nested types, leaving only a single concrete Encodable that is a trivial wrapper around a pydantic.TypeAdapter.

There are still many kinks to work out but this seems like a promising alternative to the status quo in master and #548.

eb8680 and others added 22 commits February 9, 2026 13:44
* Adding scalar encoding, make tools use signature and update fixtures

* Lint

* Lint

* More general wrappedencodable

* Lint

* Architectural fixes

* Removing embedded_type

* Lint

* Minor

* Minor fix

* Lint

* Add param_schema_type

* Minor

* New Encodable's architecture

* Recursive dataclass encodable

* Linting

* Simplify encodable

* Minor

* Fix test

* Lint

* Trim

* More trimming

* Handling images

* Lint

* Further trimming

* Updating test template formatting

* Minor

* Add MappingEncodable and cover more tests

* Lint

* More precise BaseEncodable

* Lint

* Removing adapter from base

* Move wrapped model to a staticmethod

* More concise handling of empty lists

* Minor

* Use nested_type as _wrapped_model is resolved

* Remove DTC_IDS

* Minor fix

* Special typeddict handling

* Complex Encodable

* Removing casts

* Add Encoding/Integration Parameterized Tests (#582)

* Adding tests

* Lint

* More lint

* Minor

* Removing helper functions

* Check deserialize invariant

* Minor fix for typeddict

* Better pydantic-based check

* Lint

* Test ToolSchema

* Inline completion with tools

* More inlining

* reimport requires_openai

* Removing casts

* mark xfail some tests agian

* Merging integration test into encoding

* Enforce Tool and Image to be PydanticBase model

* Update fixtures

* Update minor fixture
Base automatically changed from eb-remove-response to master February 23, 2026 01:30
datvo06 added a commit that referenced this pull request Mar 23, 2026
Three fixes in encoding.py:

1. TupleEncodable.encode() returns a TupleItems model instance (not a
   raw tuple), and deserialize() returns the model directly. This fixes
   pydantic validation in litellm integration tests for NamedTuple and
   fixed-tuple types. Extract shared field access into _extract_items().

2. Add _TupleSafeJsonSchema that overrides pydantic's tuple_schema() to
   produce object schemas (item_0, item_1 properties) instead of
   prefixItems arrays. Applied via _BoxEncoding.model_json_schema() so
   dataclasses containing tuple fields produce OpenAI-compatible schemas.
   Can be removed once #584 replaces the Encodable system.

3. SequenceEncodable.encode()/deserialize() return lists (not tuples) to
   preserve encode idempotency — nested_type on a list dispatches to the
   sequence encoder, avoiding a mismatch with TupleEncodable.
datvo06 added a commit that referenced this pull request Mar 23, 2026
Use Pydantic's Annotated extension points (PlainValidator, PlainSerializer,
WithJsonSchema) to handle tuples — adapted from _pydantic_type_tuple in #584.

- _safe_tuple_type: rewrites fixed-length tuple[T1, T2, ...] into an
  Annotated type with custom validation (item_N dict → tuple), serialization
  (tuple → item_N dict), and JSON schema (object with item_0/item_1
  properties instead of prefixItems). Single mechanism for schema, validation,
  and serialization.

- _rewrite_tuple_annotations: recursively applies _safe_tuple_type to
  dataclass fields, creating an Annotated proxy so nested tuples inside
  objects also get safe schemas.

- TupleEncodable.encode() returns TupleItems model instances; deserialize()
  returns the model directly. Shared field access via _extract_items().

- SequenceEncodable.encode()/deserialize() return lists to preserve encode
  idempotency with the new TupleEncodable return type.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants